﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Device.Location;
using System.Linq;
using System.Reflection;
using Ninject;
using SydEnergiPrototype.DataRepositoryServiceRef;
using SydEnergiPrototype.Model;

namespace SydEnergiPrototype.Logic
{
    public class CabinetParser : Parser
    {
        private readonly IParserFactory _parserFactory;

        [Inject]
        public CabinetParser(IParserFactory parserFactory)
        {
            if(parserFactory == null)
                throw new ArgumentNullException("parserFactory");
            _parserFactory = parserFactory;
        }

        protected override bool IsPrimitive(PropertyInfo property)
        {
            return false;
        }

        protected override void SerializeNonPrimitive(PropertyInfo property, object source, object returnObject)
        {
            object o = property.GetValue(source, null);
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var returnObjectInfos = returnObject.GetType().GetProperties(flags);

            if(property.PropertyType == typeof(Entries))
            {
                foreach (var returnObjectInfo in returnObjectInfos.Where(i => i.Name == "Entries"))
                {
                    var parser = _parserFactory.Make(typeof(EntryDto));
                    var entries = new ObservableCollection<EntryDto>();
                    foreach (var entry in (source as ICabinet).EntryCollection)
                    {
                        entries.Add(parser.ParseToDto<IEntry, EntryDto>(entry));
                    }
                    returnObjectInfo.SetValue(returnObject, entries, null);
                }
            }
            else if(property.PropertyType == typeof(IAddress))
            {
                foreach (var returnObjectInfo in returnObjectInfos.Where(i => i.Name == "AddressDto"))
                {
                    var parser = _parserFactory.Make(typeof (Address));
                    var addressDto = parser.ParseToDto<IAddress, AddressDto>(o as Address);
                    returnObjectInfo.SetValue(returnObject, addressDto, null);
                }
            }
            else if(property.PropertyType == typeof(GeoCoordinate))
            {
                var ro = returnObject as CabinetDto;
                var geoCoordinate = o as GeoCoordinate;
                ro.Latitude = geoCoordinate.Latitude;
                ro.Longitude = geoCoordinate.Longitude;
            }
            else if(property.Name == "Id")
            {
                var ro = returnObject as CabinetDto;
                ro.CabinetId = property.GetValue(source, null) as string;
            }

        }

        protected override void DeserializeNonPrimitive(PropertyInfo property, object source, object returnObject)
        {
            object o = property.GetValue(source, null);
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var returnObjectInfos = returnObject.GetType().GetProperties(flags);

            if (property.PropertyType == typeof(ObservableCollection<EntryDto>))
            {
                foreach (var returnObjectInfo in returnObjectInfos.Where(i => i.Name == "EntryCollection"))
                {
                    var parser = _parserFactory.Make(typeof(EntryDto));
                    var entries = new Entries();
                    foreach (var entry in (source as CabinetDto).Entries)
                    {
                        entries.Add(parser.ParseFromDto<EntryDto, Entry>(entry));
                    }
                    returnObjectInfo.SetValue(returnObject, entries, null);
                }
            }
            else if (property.PropertyType == typeof(AddressDto))
            {
                foreach (var returnObjectInfo in returnObjectInfos.Where(i => i.Name == "CabinetAddress"))
                {
                    var parser = _parserFactory.Make(typeof(AddressDto));
                    var address = parser.ParseFromDto<AddressDto, Address>(o as AddressDto);
                    returnObjectInfo.SetValue(returnObject, address, null);
                }
            }
            else if (property.PropertyType == typeof(double) && (property.Name == "Longitude" || property.Name == "Latitude"))
            {
                var d = (double)o;

                var ro = returnObject as Cabinet;
                if(ro.CabinetPosition == null)
                    ro.CabinetPosition = new GeoCoordinate();

                foreach (var prop in ro.CabinetPosition.GetType().GetProperties(flags).Where(i => i.Name == property.Name))
                {
                    prop.SetValue(ro.CabinetPosition, d, null);
                }
            }
            else if (property.Name == "CabinetId")
            {
                var ro = returnObject as Cabinet;
                foreach (var returnObjectInfo in returnObjectInfos.Where(i => i.Name == "Id"))
                {
                    returnObjectInfo.SetValue(ro, o, null);
                }
            }
        }
    }
}
